import account
import config
import os
from utils import *
import pickle
import information
import database
import json
import hashlib
from Crypto.Util import randpool

#TODO : verifier la bonne serialisation du group all

class friendError(Exception):
    def __init__(self, value):
        self.value = value
        errorOutput(value)
    def __str__(self):
        return repr(self.value)
    
class friend(account.Account):
    def __init__(self,values={}):
        return account.Account.__init__(self,values)
    
    def getGroups(self):
        """return a list of friendsGroup object to which friend belongs"""
        result = []
        for group in getFriendsPool().getAllFriendsGroup():
            if group.isMember(self):
                result.append(group)
        return result
        
        
class friendsGroup():
    """class that defines group of friends"""
    def __init__(self,liste,name=None,ID=None):
        """liste should be a list of friend objects"""
        self._friendsList = liste
        self._name = name
        self._ID = ID
        
    def __iter__(self):
        return self._friendsList.__iter__()
    
    def getName(self):
        return self._name
    
    def getID(self):
        return self._ID
    
    def append(self,friend):
        """append a friend object to the group"""
        self._friendsList.append(friend)
        return True
    
    def remove(self,friend):
        """remove a friend object to the group
        return false if friend not found"""
        for f in self:
            if f == friend:
                self._friendsList.remove(f)
                return True
        return False
    
    def appendByID(self,friendID):
        """append a friend object to the group"""
        friendObject = getGlobalAccount(friendID)
        self.append(friendObject)
        return True
    
    def isMember(self,friend):
        """test if friend object is a member of the group"""
        for f in self:
            if f == friend:
                return True
                break
        return False
    
    def dumps(self):
        """dump group object to string"""
        dicForJson = {}
        dicForJson["name"] = self._name
        dicForJson["id"] = self._ID
        dicForJson["friends"] = []
        for f in self:
            dicForJson["friends"].append(f.getID())
        return json.dumps(dicForJson)
    
    def loads(self,jsonString):
        """func that import group data from JSON string"""
        dicJson = json.loads(jsonString)
        if "name" in dicJson.keys():
            self._name = dicJson["name"]
        if "id" in dicJson.keys():
            self._ID = dicJson["id"]
        if "friends" in dicJson.keys():
            self._friendsList = []
            for fid in dicJson["friends"]:
                self._friendsList.append(getGlobalAccount(fid))
        #special group ALL
        if self._ID == "ALL":
            self.isMember = lambda friend : True
        return True
 
    
    
class myFriendsGroup(friendsGroup):
    def append(self,friend):
        if friendsGroup.append(self,friend):
            self.save()
            return True
        else:
            return False
    
    def remove(self,friend):
        if friendsGroup.remove(self,friend):
            self.save()
            return True
        else:
            return False
        
    def save(self):
        """save friend group to base
        return true if ok"""
        return information.setPersonalInformation(information.INFO_ID_GROUPS+'/'+self.getID(),FRIENDSGROUP_ALL,self.dumps())
    
class friendsPool():
    def __init__(self):
        return
    
    def getFriend(self,ID):
        """get friend by ID
        if friend not in base, return empty friend object
        return friend object"""
        #we get raw data to disable information object building and infinite loop on friends building inside information object
        base = database.getMyDatabase()
        if base.isExistInformation(information.INFO_ID_CONTACT,ID):
            data = base.getRawData(information.INFO_ID_CONTACT,ID)
            #convert data to account object
            acc = friend({})
            acc.loads(data)
            return acc
        else:
            if ID == "ALL":
                return FRIEND_ALL
            return friend({'id':ID})

    
    def getAllFriends(self):
        """return a friendsGroup object containg all known and approved friends (including you!)"""
        #search for contacts directly in db
        base = database.getMyDatabase()
        result = base.searchInformations(information.INFO_ID_CONTACT,"%")
        #convert info to account object
        friendResult = friendsGroup([])
        for infoid,authorid in result:
            friendResult.append(self.getFriend(authorid))
        return friendResult
    
    def getFriendsGroup(self,groupID):
        """get group by group ID
        if group not in base, return empty group object
        return friend object"""
        #we get raw data to disable infinite loop
        base = database.getMyDatabase()
        result = base.searchInformations(information.INFO_ID_GROUPS+'/'+groupID,"%")
        if len(result) == 1:
            data = base.getRawData(result[0][0],result[0][1])
            #convert data to friendsgroup object
            g = friendsGroup([])
            g.loads(data)
            return g
        elif len(result) > 1:
            raise friendError("getFriendsGroup : duplicate group ID %s in base."%groupID)
        else:
            if groupID == "ALL" :
                return FRIENDSGROUP_ALL
            else:
                return friendsGroup([],name="Unknown",ID=groupID)
            
    def getMyFriendsGroup(self,groupID):
        """get group by group ID managed by the current user
        if group not in base, raise error
        return friend object"""
        #we get raw data to disable infinite loop
        base = database.getMyDatabase()
        result = base.searchInformations(information.INFO_ID_GROUPS+'/'+groupID,account.getMyAccount().getID())
        if len(result) == 1:
            data = base.getRawData(result[0][0],result[0][1])
            #convert data to friendsgroup object
            g = myFriendsGroup([])
            g.loads(data)
            return g
        elif len(result) > 1:
            raise friendError("getMyFriendsGroup : duplicate group ID %s in base."%groupID)
        else:
            raise friendError("getMyFriendsGroup : group ID %s not in base."%groupID)
            
    def getAllFriendsGroup(self):
        """return a lis of friendsGroup object"""
        #search for groups directly in db
        base = database.getMyDatabase()
        result = base.searchInformations(information.INFO_ID_GROUPS+'/%',"%")
        #convert info to friendsgroup objects
        res = []
        for infoid,authorid in result:
            groupid = infoid.replace(information.INFO_ID_GROUPS+'/','')
            res.append(self.getFriendsGroup(groupid))
        return res
           
    def delFriend(self,ID):
        """delete friend in base"""
        base = database.getMyDatabase()
        return base.delInformation(information.INFO_ID_CONTACT,ID)
    
    def importFriendFromFile(self,filePath):
        info = information.loadInformationFromFile(filePath,checkSig=False)
        information.setInformation(info)
        return True
    
    def exportFriendToFile(self,friend,fileName):
        info = base.getInformation(information.INFO_ID_CONTACT,friend.getID())
        info.exportToFile(fileName)  
        return True
        
    def createGroup(self,groupName):
        """create a new group manage by the user (my) and store it in base
        return a friendsgroup object"""
        #create a new object group
        pool = randpool.RandomPool()
        groupID = hashlib.sha1(pool.get_bytes(1024)).hexdigest()
        newg = myFriendsGroup([],groupName,groupID)
        #store it in base
        #FIXME : good rights to access group info ?
        information.setPersonalInformation(information.INFO_ID_GROUPS+'/'+groupID,FRIENDSGROUP_ALL,newg.dumps())
        #return the group
        return newg

pool = None

def getFriendsPool():
    """return the current friendsPool object in use"""
    global pool
    if not pool:
        pool = friendsPool()
    return pool


def getGlobalAccount(ID):
    """return account object based on ID
    if account not in base, return empty friend object"""
    if ID == account.getMyAccount().getID():
        return account.getMyAccount()
    else:
        return getFriendsPool().getFriend(ID)
    
#define constant special group "ALL" : all known/trusted friends
FRIEND_ID_ALL = "ALL"
FRIEND_ALL = friend({"id":FRIEND_ID_ALL})
FRIENDSGROUP_ALL = friendsGroup([FRIEND_ALL,],name="Everybody",ID="ALL")
FRIENDSGROUP_ALL.isMember = lambda friend : True
    